#include "zlog.h"
#include "server_sq.h"

#include <stdio.h>    
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <signal.h>
#include <getopt.h>
#include <libgen.h>

#define IPADDR       "127.0.0.1"
#define PORT          8999
#define MAX_CLIENT    100         //最大连接数
#define DATABASE     "server.db"
#define TABLE        "SERVER_TEMP"

void sig_handle(int signum);
int server_socket(char *ipaddr,int port);
static inline void print_help(char *progname);
int       g_stop = 0;

int main(int argc,char **argv)
{
	int                       serv_socket_fd = -1;
	int                       serv_accept_fd = -1;
	int                       serv_read_rv = -1;
	struct sockaddr_in        cli_addr;
	socklen_t                 cli_addrlen;
	char                      buf[1024];

	int                       i;
	int                       epollfd;
	int                       epollwait;
	struct epoll_event        event;
	struct epoll_event        evlist[MAX_CLIENT];
	struct client_data        data;
	char                      separator = '-';

	char                      progname[20];
	int                       opt;
	int                       daemon_run = 0;
	int                       port;
	char                      ipaddr[20];    //ip地址
	sqlite3                  *db;

	int                       rc;             //zlog
	zlog_category_t          *c;              //zlog



	//命令行参数
	memset(progname,0,sizeof(progname));
	memset(ipaddr,0,sizeof(ipaddr));

	memset(&data,0,sizeof(data));
	//默认参数信息
	port = PORT;
	strcpy(progname,basename(argv[0]));
	strcpy(ipaddr,IPADDR);

	/*--------------------------------*
	 *        命令行参数解析          *
	 *--------------------------------*/	
	struct option long_options[] = {
		{"daemon",no_argument,NULL,'b'},
		{"IDADDR",required_argument,NULL,'i'},
		{"PORT",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{"NULL",0,NULL,0}
	};
	while((opt = getopt_long(argc,argv,"bi:p:h",long_options,NULL))!= -1)
	{
		switch(opt)
		{
			case 'b':
				daemon_run = 1;
				break;
			case 'i':
				strcpy(ipaddr,optarg);
				break;
			case 'p':
				port = atoi(optarg);
				break;
			case 'h':
				print_help(progname);
				return EXIT_SUCCESS;
			default :
				break;
		}
	}
	//后台
	if(daemon_run)
	{
		daemon(0,0);
	}
	//zlog
	rc = zlog_init("./server.conf");
	if (rc) {
		printf("init failed:%s\n",strerror(errno));
		return -1;
	}
	c = zlog_get_category("my_cat");
	if (!c) {
		printf("Get cat fail\n");
		zlog_fini();
		return -2;
	}


	//打开数据库
	db = create_database();
	if(!db)
	{
		printf("Can't open database:%s\n",strerror(errno));
		return -3;;
	}
	printf("Opened database successfully\n");


	/*--------------------------------*                        
	 *            建立连接            *                      
	 *--------------------------------*/
	cli_addrlen = sizeof(cli_addr);
	memset(buf,0,sizeof(buf));
	serv_socket_fd = server_socket(ipaddr,port);

	epollfd = epoll_create(MAX_CLIENT);
	if(epollfd < 0)
	{
		zlog_error(c,"epoll create failure:%s",strerror(errno));
		close(serv_socket_fd);
		sqlite3_close(db);
		return -5;
	}

	event.data.fd = serv_socket_fd;
	event.events = EPOLLIN;
	if(epoll_ctl(epollfd,EPOLL_CTL_ADD,event.data.fd,&event) < 0)
	{
		zlog_error(c,"epoll add serv_socket_fd failure:%s",strerror(errno));
		close(serv_socket_fd);
		sqlite3_close(db);
		return -6;
	}

	//安装信号
	signal(SIGTERM,sig_handle);

	/*--------------------------------*                        
	 *         epoll多路复用          *                    
	 *--------------------------------*/
	while(!g_stop)
	{
		epollwait = epoll_wait(epollfd,evlist,MAX_CLIENT,-1);
		if(-1 == epollwait)
		{	
			if(errno!=EINTR)
			{
				zlog_error(c,"epoll failure:%s",strerror(errno));
				break;
			}

		}
		else if(epollwait == 0)
		{
			continue;
		}

		//遍历就绪状态的客户端
		for(i=0; i<epollwait; i++)
		{
			if((evlist[i].events&EPOLLERR) || (evlist[i].events&EPOLLHUP))
			{
				zlog_error(c,"epoll_wait get error on fd[%d]:%s",evlist[i].data.fd,strerror(errno));
				epoll_ctl(epollfd,EPOLL_CTL_DEL,evlist[i].data.fd,NULL);
				close(evlist[i].data.fd);
			}
			//新的客户端到达
			if(evlist[i].data.fd == serv_socket_fd)
			{
				serv_accept_fd = accept(serv_socket_fd,(struct sockaddr*)&cli_addr,&cli_addrlen);
				if(serv_accept_fd < 0)
				{
					zlog_error(c,"Accept client failure:%s",strerror(errno));		
					continue;	
				}
				zlog_info(c,"Accept client success");

				event.data.fd = serv_accept_fd;
				event.events = EPOLLIN;
				if(epoll_ctl(epollfd,EPOLL_CTL_ADD,serv_accept_fd,&event) < 0)
				{
					zlog_error(c,"epoll add client socket failure:%s",strerror(errno));
					close(evlist[i].data.fd);
					continue;
				}
			}
			//已经建立连接的客户端
			else
			{
				serv_read_rv = read(evlist[i].data.fd,buf,sizeof(buf));
				//获得数据中的客户端编号
				if(split(buf,&separator,&data) < 0)
				{
					zlog_error(c,"Split failure:%s\n",strerror(errno));
					return -1;
				}
				if(serv_read_rv < 0)	
				{		
					zlog_error(c,"socket[%d] read failure:%s",evlist[i].data.fd,strerror(errno));
					epoll_ctl(epollfd,EPOLL_CTL_DEL,evlist[i].data.fd,NULL);
					close(evlist[i].data.fd);
					continue;	
				}
				else if(serv_read_rv == 0)
				{
					zlog_notice(c,"Client:%s get disconnect and will be removed",data.id);
					epoll_ctl(epollfd,EPOLL_CTL_DEL,evlist[i].data.fd,NULL);
					close(evlist[i].data.fd);
					continue;	

				}
				else{
					printf("Read from client:%s successfully:%s\n",data.id,buf);
					if(insert_temp(buf,db) < 0)
					{
						zlog_error(c,"Save data to table failure:%s",strerror(errno));
						break;
					}
					zlog_info(c,"Save data to table successfully!:%s",buf);

				}
			}
		}
	}

	//程序退出提示
	zlog_info(c,"Power off server");
	printf("Power off server\n");
	zlog_fini();
	sqlite3_close(db);
	close(epollfd);
	close(serv_socket_fd);
	return 0;
}


/**************************************************
 * 函数名：   server_socket
 *
 * 函数功能： 创建socket套接字，绑定和监听端口
 *
 * 参数：     ipaddr     ip地址
 *            port       端口  
 *
 * 返回值：   成功返回0，失败返回负数
 **************************************************/

int server_socket(char *ipaddr,int port)
{
	int                       serv_socket_fd = -1;
	struct sockaddr_in        serv_addr;
	int                       opt = 1;

	if(!ipaddr || port <= 0)
	{
		return -1;
	}


	serv_socket_fd = socket(AF_INET,SOCK_STREAM,0);
	if(serv_socket_fd < 0)
	{
		printf("Create server socket failure:%s\n",strerror(errno));
		return -1;
	}
	printf("Creat server socket successfully\n");

	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(port);
	inet_aton(ipaddr,&serv_addr.sin_addr);

	if(setsockopt(serv_socket_fd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt)) < 0)
	{
		printf("setsockopt failure:%s\n",strerror(errno));
		close(serv_socket_fd);
		return -1;
	}


	if(bind(serv_socket_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0)	
	{
		printf("Bind to server failure:%s\n",strerror(errno));
		close(serv_socket_fd);
		return -2;
	}
	printf("Bind to server successfully\n");


	if(listen(serv_socket_fd,MAX_CLIENT) < 0)
	{	
		printf("Listen to client failure:%s\n",strerror(errno));	
		close(serv_socket_fd);
		return -3;
	}
	printf("Listen on port successfully\n");

	return serv_socket_fd;
}

/**************************************************
 * 函数名：   sig_handle
 *
 * 函数功能： 将g_stop设为1，结束while循环使主函数退出
 *
 * 参数：     signum          信号
 *
 * 返回值：   成功返回0，输入参数有误返回-1
 **************************************************/

void sig_handle(int signum)        
{              
	printf("Catch signal [%d]\n",signum);	
	g_stop = 1;

}


/**************************************************
 * 函数名：   printf_help
 *
 * 函数功能： 打印帮助信息
 *
 * 参数：     progname        功能名
 *
 * 返回值：   无返回值
 **************************************************/

static inline void print_help(char *progname)
{
	printf("\n*******************************************Help******************************************\n");
	printf("USAGE:%s [option]...\n",progname);
	printf("%s is a server program,which can be used to catch temperature from the client\n",progname);
	printf("\nMandatory argument to long option are mandatory for short options too:\n");
	printf("-b[daemon]   set program running on background\n");
	printf("-i[IDADDR]   set the IP address of the server progname\n");
	printf("-p[PORT]     set the PORT of the server progname\n");
	printf("-h[help]     Display this help information\n");

	printf("\nExample:%s -b -p 8888\n",progname);
	printf("*****************************************************************************************\n");
	printf("\n");
	return;
}

